"""BLE Offline Configuration Service.

Provides GATT characteristics for offline configuration management including
offline queue control, sync operations, and network status monitoring.
"""

import asyncio
import json
import logging
import struct
import time
import uuid
from typing import Dict, List, Optional, Callable, Any, Union
from enum import Enum

from ...config.offline_manager import (
    OfflineConfigManager, ConfigStatus, NetworkStatus, OperationType
)
from ...config.config_synchronizer import (
    ConfigSynchronizer, SyncStrategy, SyncDirection, ConflictResolutionStrategy
)

logger = logging.getLogger(__name__)


# Offline Service UUID: FF65
OFFLINE_SERVICE_UUID = "0000FF65-0000-1000-8000-00805F9B34FB"
OFFLINE_CONTROL_UUID = "0000FF66-0000-1000-8000-00805F9B34FB"      # Write
OFFLINE_STATUS_UUID = "0000FF67-0000-1000-8000-00805F9B34FB"       # Read, Notify
SYNC_CONTROL_UUID = "0000FF68-0000-1000-8000-00805F9B34FB"         # Write
CONFLICT_DATA_UUID = "0000FF69-0000-1000-8000-00805F9B34FB"        # Read, Write
QUEUE_STATUS_UUID = "0000FF6A-0000-1000-8000-00805F9B34FB"         # Read, Notify


class OfflineCommand(Enum):
    """Offline configuration control commands."""
    QUEUE_CONFIG = 0x01             # Queue configuration for offline processing
    CANCEL_CONFIG = 0x02            # Cancel queued configuration
    RETRY_FAILED = 0x03             # Retry failed configurations
    GET_QUEUE_STATUS = 0x04         # Get queue status
    CLEAR_QUEUE = 0x05              # Clear configuration queue
    SET_NETWORK_MODE = 0x06         # Set network mode (auto/manual)
    GET_OFFLINE_STATS = 0x07        # Get offline statistics


class SyncCommand(Enum):
    """Synchronization control commands."""
    START_SYNC = 0x01               # Start synchronization
    STOP_SYNC = 0x02                # Stop active synchronization
    FORCE_SYNC = 0x03               # Force immediate sync
    GET_SYNC_STATUS = 0x04          # Get sync status
    SET_SYNC_CONFIG = 0x05          # Configure sync parameters
    RESOLVE_CONFLICTS = 0x06        # Resolve conflicts
    GET_SYNC_HISTORY = 0x07         # Get synchronization history


class ConflictCommand(Enum):
    """Conflict management commands."""
    LIST_CONFLICTS = 0x01           # List unresolved conflicts
    GET_CONFLICT_DETAILS = 0x02     # Get conflict details
    RESOLVE_CONFLICT = 0x03         # Resolve single conflict
    BATCH_RESOLVE = 0x04            # Batch resolve conflicts
    AUTO_RESOLVE_ALL = 0x05         # Auto-resolve all conflicts
    EXPORT_CONFLICTS = 0x06         # Export conflict data


class OfflineOperationStatus(Enum):
    """Offline operation status."""
    IDLE = 0x00                     # No active operation
    PROCESSING = 0x01               # Operation in progress
    SUCCESS = 0x02                  # Operation completed successfully
    ERROR = 0x03                    # Operation failed
    NETWORK_ERROR = 0x04            # Network connectivity error
    SYNC_ERROR = 0x05               # Synchronization error
    CONFLICT_ERROR = 0x06           # Conflict resolution error


class OfflineService:
    """BLE Offline Configuration Service implementation."""
    
    def __init__(self, offline_manager: OfflineConfigManager,
                 config_synchronizer: ConfigSynchronizer):
        """Initialize offline service.
        
        Args:
            offline_manager: Offline configuration manager instance
            config_synchronizer: Configuration synchronizer instance
        """
        self.offline_manager = offline_manager
        self.config_synchronizer = config_synchronizer
        
        # Service state
        self.current_operation_id: Optional[str] = None
        self.operation_status = OfflineOperationStatus.IDLE
        self.operation_progress = 0.0
        self.operation_result: Optional[Dict[str, Any]] = None
        self.operation_error: Optional[str] = None
        
        # Active sync operation
        self.active_sync_id: Optional[str] = None
        
        # Notification callback
        self.notify_callback: Optional[Callable[[str, bytes], None]] = None
        
        # Service registration data
        self.service_data = self._create_service_definition()
        
        # Status notification task
        self.status_task: Optional[asyncio.Task] = None
        self.status_notify_interval = 5  # 5 seconds
        
        # Queue monitoring task
        self.queue_monitor_task: Optional[asyncio.Task] = None
        self.queue_monitor_interval = 10  # 10 seconds
        
        # Statistics
        self.stats = {
            "operations_processed": 0,
            "configs_queued": 0,
            "sync_operations": 0,
            "conflicts_resolved": 0,
            "errors_encountered": 0
        }
        
        logger.info("OfflineService initialized")
    
    def _create_service_definition(self) -> Dict[str, Any]:
        """Create GATT service definition."""
        return {
            "uuid": OFFLINE_SERVICE_UUID,
            "primary": True,
            "characteristics": [
                {
                    "uuid": OFFLINE_CONTROL_UUID,
                    "flags": ["write"],
                    "value": bytearray(512),  # Command data
                    "descriptors": []
                },
                {
                    "uuid": OFFLINE_STATUS_UUID,
                    "flags": ["read", "notify"],
                    "value": bytearray(64),   # Status information
                    "descriptors": [
                        {
                            "uuid": "00002902-0000-1000-8000-00805F9B34FB",  # CCC
                            "flags": ["read", "write"],
                            "value": bytearray([0x00, 0x00])
                        }
                    ]
                },
                {
                    "uuid": SYNC_CONTROL_UUID,
                    "flags": ["write"],
                    "value": bytearray(256),  # Sync commands
                    "descriptors": []
                },
                {
                    "uuid": CONFLICT_DATA_UUID,
                    "flags": ["read", "write"],
                    "value": bytearray(1024), # Conflict data
                    "descriptors": []
                },
                {
                    "uuid": QUEUE_STATUS_UUID,
                    "flags": ["read", "notify"],
                    "value": bytearray(128),  # Queue status
                    "descriptors": [
                        {
                            "uuid": "00002902-0000-1000-8000-00805F9B34FB",  # CCC
                            "flags": ["read", "write"],
                            "value": bytearray([0x00, 0x00])
                        }
                    ]
                }
            ]
        }
    
    async def start(self):
        """Start offline service."""
        # Start periodic status notifications
        self.status_task = asyncio.create_task(self._status_notification_loop())
        
        # Start queue monitoring
        self.queue_monitor_task = asyncio.create_task(self._queue_monitor_loop())
        
        logger.info("OfflineService started")
    
    async def stop(self):
        """Stop offline service."""
        if self.status_task:
            self.status_task.cancel()
            try:
                await self.status_task
            except asyncio.CancelledError:
                pass
        
        if self.queue_monitor_task:
            self.queue_monitor_task.cancel()
            try:
                await self.queue_monitor_task
            except asyncio.CancelledError:
                pass
        
        # Cancel any active operations
        if self.current_operation_id:
            await self._cancel_current_operation("Service stopped")
        
        logger.info("OfflineService stopped")
    
    # GATT Characteristic Handlers
    
    async def handle_write_offline_control(self, value: bytes) -> bool:
        """Handle write to OFFLINE_CONTROL characteristic."""
        try:
            if not value or len(value) < 1:
                return False
            
            command = OfflineCommand(value[0])
            params = value[1:] if len(value) > 1 else b''
            
            # Start new operation
            operation_id = str(uuid.uuid4())
            self.current_operation_id = operation_id
            self.operation_status = OfflineOperationStatus.PROCESSING
            self.operation_progress = 0.0
            self.operation_result = None
            self.operation_error = None
            
            # Handle command asynchronously
            asyncio.create_task(self._handle_offline_command(command, params, operation_id))
            
            return True
            
        except ValueError as e:
            logger.warning(f"Invalid offline command: {e}")
            return False
        except Exception as e:
            logger.error(f"Error handling offline control: {e}")
            return False
    
    async def handle_read_offline_status(self) -> bytes:
        """Handle read from OFFLINE_STATUS characteristic."""
        try:
            # Get network status
            network_status = self.offline_manager.network_monitor.status
            
            # Get queue statistics
            queue_status = await self.offline_manager.get_queue_status()
            
            # Format status information
            status_data = struct.pack(
                '>16s B B B H H H f',
                (self.current_operation_id or 'idle').encode('utf-8')[:16].ljust(16, b'\x00'),
                self.operation_status.value,
                network_status.value,
                int(self.operation_progress),
                queue_status['pending_count'],
                queue_status['processing_count'],
                queue_status['failed_count'],
                time.time()
            )
            
            return status_data
            
        except Exception as e:
            logger.error(f"Error reading offline status: {e}")
            return b'\x00' * 32
    
    async def handle_write_sync_control(self, value: bytes) -> bool:
        """Handle write to SYNC_CONTROL characteristic."""
        try:
            if not value or len(value) < 1:
                return False
            
            command = SyncCommand(value[0])
            params = value[1:] if len(value) > 1 else b''
            
            # Handle sync command asynchronously
            asyncio.create_task(self._handle_sync_command(command, params))
            
            return True
            
        except ValueError as e:
            logger.warning(f"Invalid sync command: {e}")
            return False
        except Exception as e:
            logger.error(f"Error handling sync control: {e}")
            return False
    
    async def handle_write_conflict_data(self, value: bytes) -> bool:
        """Handle write to CONFLICT_DATA characteristic."""
        try:
            if not value or len(value) < 1:
                return False
            
            command = ConflictCommand(value[0])
            params = value[1:] if len(value) > 1 else b''
            
            # Handle conflict command asynchronously
            asyncio.create_task(self._handle_conflict_command(command, params))
            
            return True
            
        except ValueError as e:
            logger.warning(f"Invalid conflict command: {e}")
            return False
        except Exception as e:
            logger.error(f"Error handling conflict data: {e}")
            return False
    
    async def handle_read_conflict_data(self) -> bytes:
        """Handle read from CONFLICT_DATA characteristic."""
        try:
            # Return stored conflict data or empty response
            if hasattr(self, 'cached_conflict_data'):
                return self.cached_conflict_data
            else:
                return json.dumps({"conflicts": []}).encode('utf-8')
            
        except Exception as e:
            logger.error(f"Error reading conflict data: {e}")
            return json.dumps({"error": "Failed to read conflicts"}).encode('utf-8')
    
    async def handle_read_queue_status(self) -> bytes:
        """Handle read from QUEUE_STATUS characteristic."""
        try:
            queue_status = await self.offline_manager.get_queue_status()
            
            # Format queue status
            status_data = struct.pack(
                '>B H H H H I f',
                queue_status.get('network_status', 'offline') == 'online',
                queue_status['pending_count'],
                queue_status['processing_count'],
                queue_status['failed_count'],
                len(self.stats),
                self.stats['configs_queued'],
                time.time()
            )
            
            return status_data
            
        except Exception as e:
            logger.error(f"Error reading queue status: {e}")
            return b'\x00' * 24
    
    # Command Handlers
    
    async def _handle_offline_command(self, command: OfflineCommand, 
                                    params: bytes, operation_id: str):
        """Handle offline command asynchronously."""
        try:
            if operation_id != self.current_operation_id:
                return  # Operation was cancelled
            
            if command == OfflineCommand.QUEUE_CONFIG:
                await self._handle_queue_config(params)
            
            elif command == OfflineCommand.CANCEL_CONFIG:
                await self._handle_cancel_config(params)
            
            elif command == OfflineCommand.RETRY_FAILED:
                await self._handle_retry_failed(params)
            
            elif command == OfflineCommand.GET_QUEUE_STATUS:
                await self._handle_get_queue_status()
            
            elif command == OfflineCommand.CLEAR_QUEUE:
                await self._handle_clear_queue(params)
            
            elif command == OfflineCommand.SET_NETWORK_MODE:
                await self._handle_set_network_mode(params)
            
            elif command == OfflineCommand.GET_OFFLINE_STATS:
                await self._handle_get_offline_stats()
            
            else:
                raise ValueError(f"Unknown offline command: {command}")
            
            # Operation completed successfully
            self.operation_status = OfflineOperationStatus.SUCCESS
            self.operation_progress = 100.0
            self.stats["operations_processed"] += 1
            
        except Exception as e:
            logger.error(f"Offline command error: {e}")
            self.operation_status = OfflineOperationStatus.ERROR
            self.operation_error = str(e)
            self.stats["errors_encountered"] += 1
        
        finally:
            # Send status notification
            await self._send_status_notification()
            
            # Clear operation after delay
            await asyncio.sleep(3)
            if self.current_operation_id == operation_id:
                self.current_operation_id = None
                self.operation_status = OfflineOperationStatus.IDLE
    
    async def _handle_queue_config(self, params: bytes):
        """Handle queue configuration command."""
        if not params:
            raise ValueError("Missing configuration parameters")
        
        config_data = json.loads(params.decode('utf-8'))
        
        entry_id = await self.offline_manager.queue_config(
            device_id=config_data["device_id"],
            config_data=config_data["config_data"],
            operation_type=OperationType(config_data.get("operation_type", "update")),
            template_id=config_data.get("template_id"),
            priority=config_data.get("priority", 5)
        )
        
        self.operation_result = {"entry_id": entry_id}
        self.stats["configs_queued"] += 1
        logger.info(f"Queued configuration: {entry_id}")
    
    async def _handle_cancel_config(self, params: bytes):
        """Handle cancel configuration command."""
        if not params:
            raise ValueError("Missing entry ID")
        
        cancel_data = json.loads(params.decode('utf-8'))
        entry_id = cancel_data["entry_id"]
        
        success = await self.offline_manager.cancel_config(entry_id)
        
        if not success:
            raise ValueError(f"Failed to cancel configuration {entry_id}")
        
        self.operation_result = {"entry_id": entry_id, "cancelled": True}
        logger.info(f"Cancelled configuration: {entry_id}")
    
    async def _handle_retry_failed(self, params: bytes):
        """Handle retry failed configurations command."""
        device_filter = None
        
        if params:
            retry_data = json.loads(params.decode('utf-8'))
            device_filter = retry_data.get("device_id")
        
        retry_count = await self.offline_manager.retry_failed_configs(device_filter)
        
        self.operation_result = {"retry_count": retry_count}
        logger.info(f"Retried {retry_count} failed configurations")
    
    async def _handle_get_queue_status(self):
        """Handle get queue status command."""
        queue_status = await self.offline_manager.get_queue_status()
        self.operation_result = queue_status
    
    async def _handle_clear_queue(self, params: bytes):
        """Handle clear queue command."""
        # This would require additional implementation in offline_manager
        # For now, just simulate
        logger.info("Queue clear requested")
        self.operation_result = {"queue_cleared": True}
    
    async def _handle_set_network_mode(self, params: bytes):
        """Handle set network mode command."""
        if not params:
            raise ValueError("Missing network mode")
        
        mode_data = json.loads(params.decode('utf-8'))
        mode = mode_data["mode"]  # "auto" or "manual"
        
        # This would configure the network monitor
        logger.info(f"Network mode set to: {mode}")
        self.operation_result = {"network_mode": mode}
    
    async def _handle_get_offline_stats(self):
        """Handle get offline statistics command."""
        queue_status = await self.offline_manager.get_queue_status()
        
        offline_stats = {
            **queue_status,
            "service_stats": self.stats.copy()
        }
        
        self.operation_result = offline_stats
    
    async def _handle_sync_command(self, command: SyncCommand, params: bytes):
        """Handle synchronization command."""
        try:
            if command == SyncCommand.START_SYNC:
                await self._handle_start_sync(params)
            
            elif command == SyncCommand.STOP_SYNC:
                await self._handle_stop_sync()
            
            elif command == SyncCommand.FORCE_SYNC:
                await self._handle_force_sync(params)
            
            elif command == SyncCommand.GET_SYNC_STATUS:
                await self._handle_get_sync_status()
            
            elif command == SyncCommand.SET_SYNC_CONFIG:
                await self._handle_set_sync_config(params)
            
            elif command == SyncCommand.RESOLVE_CONFLICTS:
                await self._handle_resolve_conflicts(params)
            
            elif command == SyncCommand.GET_SYNC_HISTORY:
                await self._handle_get_sync_history()
            
            else:
                raise ValueError(f"Unknown sync command: {command}")
            
            self.stats["sync_operations"] += 1
            
        except Exception as e:
            logger.error(f"Sync command error: {e}")
    
    async def _handle_start_sync(self, params: bytes):
        """Handle start synchronization command."""
        sync_params = {}
        
        if params:
            sync_data = json.loads(params.decode('utf-8'))
            sync_params = {
                "strategy": SyncStrategy(sync_data.get("strategy", "incremental_sync")),
                "direction": SyncDirection(sync_data.get("direction", "bidirectional")),
                "device_filter": sync_data.get("device_filter"),
                "conflict_resolution": ConflictResolutionStrategy(
                    sync_data.get("conflict_resolution", "timestamp_priority")
                )
            }
        
        self.active_sync_id = await self.config_synchronizer.start_sync(**sync_params)
        logger.info(f"Started sync operation: {self.active_sync_id}")
    
    async def _handle_stop_sync(self):
        """Handle stop synchronization command."""
        if self.active_sync_id:
            success = await self.config_synchronizer.cancel_sync(self.active_sync_id)
            if success:
                logger.info(f"Stopped sync operation: {self.active_sync_id}")
                self.active_sync_id = None
    
    async def _handle_force_sync(self, params: bytes):
        """Handle force synchronization command."""
        # Force sync with full strategy
        sync_params = {
            "strategy": SyncStrategy.FULL_SYNC,
            "direction": SyncDirection.BIDIRECTIONAL
        }
        
        self.active_sync_id = await self.config_synchronizer.start_sync(**sync_params)
        logger.info(f"Started forced sync: {self.active_sync_id}")
    
    async def _handle_get_sync_status(self):
        """Handle get sync status command."""
        if self.active_sync_id:
            sync_status = await self.config_synchronizer.get_sync_status(self.active_sync_id)
            if sync_status:
                # Cache sync status for client reading
                self.cached_sync_status = json.dumps({
                    "sync_id": sync_status.sync_id,
                    "strategy": sync_status.strategy.value,
                    "direction": sync_status.direction.value,
                    "processed_items": sync_status.processed_items,
                    "total_items": sync_status.total_items,
                    "successful_items": sync_status.successful_items,
                    "failed_items": sync_status.failed_items,
                    "conflicts_detected": sync_status.conflicts_detected,
                    "conflicts_resolved": sync_status.conflicts_resolved
                }).encode('utf-8')
    
    async def _handle_set_sync_config(self, params: bytes):
        """Handle set sync configuration command."""
        if not params:
            return
        
        sync_config = json.loads(params.decode('utf-8'))
        # Store sync configuration for future operations
        self.sync_config = sync_config
        logger.info("Updated sync configuration")
    
    async def _handle_resolve_conflicts(self, params: bytes):
        """Handle resolve conflicts command."""
        if not params:
            return
        
        conflict_data = json.loads(params.decode('utf-8'))
        strategy = ConflictResolutionStrategy(
            conflict_data.get("strategy", "timestamp_priority")
        )
        
        # This would resolve conflicts through the synchronizer
        logger.info(f"Resolving conflicts with strategy: {strategy.value}")
        self.stats["conflicts_resolved"] += 1
    
    async def _handle_get_sync_history(self):
        """Handle get sync history command."""
        # Return cached sync history
        sync_history = getattr(self, 'sync_history', [])
        self.cached_sync_history = json.dumps(sync_history).encode('utf-8')
    
    async def _handle_conflict_command(self, command: ConflictCommand, params: bytes):
        """Handle conflict management command."""
        try:
            if command == ConflictCommand.LIST_CONFLICTS:
                await self._handle_list_conflicts(params)
            
            elif command == ConflictCommand.GET_CONFLICT_DETAILS:
                await self._handle_get_conflict_details(params)
            
            elif command == ConflictCommand.RESOLVE_CONFLICT:
                await self._handle_resolve_single_conflict(params)
            
            elif command == ConflictCommand.BATCH_RESOLVE:
                await self._handle_batch_resolve_conflicts(params)
            
            elif command == ConflictCommand.AUTO_RESOLVE_ALL:
                await self._handle_auto_resolve_all(params)
            
            elif command == ConflictCommand.EXPORT_CONFLICTS:
                await self._handle_export_conflicts(params)
            
            else:
                raise ValueError(f"Unknown conflict command: {command}")
            
        except Exception as e:
            logger.error(f"Conflict command error: {e}")
    
    async def _handle_list_conflicts(self, params: bytes):
        """Handle list conflicts command."""
        # Mock conflict data - in real implementation would query actual conflicts
        conflicts = [
            {
                "conflict_id": "conflict_001",
                "device_id": "device_123",
                "field_path": "wifi.ssid",
                "local_value": "LocalNetwork",
                "remote_value": "RemoteNetwork",
                "severity": "medium"
            }
        ]
        
        self.cached_conflict_data = json.dumps({
            "operation": "list_conflicts",
            "conflicts": conflicts,
            "total_count": len(conflicts)
        }).encode('utf-8')
    
    async def _handle_get_conflict_details(self, params: bytes):
        """Handle get conflict details command."""
        if not params:
            return
        
        details_data = json.loads(params.decode('utf-8'))
        conflict_id = details_data["conflict_id"]
        
        # Mock detailed conflict data
        conflict_details = {
            "conflict_id": conflict_id,
            "device_id": "device_123",
            "field_path": "wifi.ssid",
            "local_value": "LocalNetwork",
            "remote_value": "RemoteNetwork",
            "local_timestamp": "2025-01-08T12:00:00Z",
            "remote_timestamp": "2025-01-08T13:00:00Z",
            "conflict_type": "VALUE",
            "severity": "medium",
            "description": "WiFi SSID value conflict"
        }
        
        self.cached_conflict_data = json.dumps(conflict_details).encode('utf-8')
    
    async def _handle_resolve_single_conflict(self, params: bytes):
        """Handle resolve single conflict command."""
        if not params:
            return
        
        resolve_data = json.loads(params.decode('utf-8'))
        conflict_id = resolve_data["conflict_id"]
        resolution = resolve_data["resolution"]  # "local", "remote", or specific value
        
        logger.info(f"Resolved conflict {conflict_id} with: {resolution}")
        self.stats["conflicts_resolved"] += 1
    
    async def _handle_batch_resolve_conflicts(self, params: bytes):
        """Handle batch resolve conflicts command."""
        if not params:
            return
        
        batch_data = json.loads(params.decode('utf-8'))
        conflicts = batch_data["conflicts"]
        strategy = batch_data.get("strategy", "timestamp_priority")
        
        resolved_count = len(conflicts)
        logger.info(f"Batch resolved {resolved_count} conflicts with {strategy}")
        self.stats["conflicts_resolved"] += resolved_count
    
    async def _handle_auto_resolve_all(self, params: bytes):
        """Handle auto-resolve all conflicts command."""
        strategy = "timestamp_priority"
        
        if params:
            auto_data = json.loads(params.decode('utf-8'))
            strategy = auto_data.get("strategy", strategy)
        
        # Mock auto-resolution
        resolved_count = 5  # Mock number
        logger.info(f"Auto-resolved all conflicts with {strategy}")
        self.stats["conflicts_resolved"] += resolved_count
    
    async def _handle_export_conflicts(self, params: bytes):
        """Handle export conflicts command."""
        export_format = "json"
        
        if params:
            export_data = json.loads(params.decode('utf-8'))
            export_format = export_data.get("format", export_format)
        
        # Mock export data
        export_result = {
            "conflicts": [],
            "export_format": export_format,
            "exported_at": time.time()
        }
        
        self.cached_conflict_data = json.dumps(export_result).encode('utf-8')
        logger.info(f"Exported conflicts in {export_format} format")
    
    # Notification Methods
    
    async def _send_status_notification(self):
        """Send offline status notification."""
        try:
            status_data = await self.handle_read_offline_status()
            await self._send_notification(OFFLINE_STATUS_UUID, status_data)
        except Exception as e:
            logger.error(f"Error sending status notification: {e}")
    
    async def _send_queue_notification(self):
        """Send queue status notification."""
        try:
            queue_data = await self.handle_read_queue_status()
            await self._send_notification(QUEUE_STATUS_UUID, queue_data)
        except Exception as e:
            logger.error(f"Error sending queue notification: {e}")
    
    async def _send_notification(self, characteristic_uuid: str, data: bytes):
        """Send notification for a characteristic."""
        if self.notify_callback:
            self.notify_callback(characteristic_uuid, data)
    
    # Background Tasks
    
    async def _status_notification_loop(self):
        """Background status notification loop."""
        while True:
            try:
                await asyncio.sleep(self.status_notify_interval)
                
                # Send status update if operation is active
                if (self.current_operation_id and 
                    self.operation_status == OfflineOperationStatus.PROCESSING):
                    await self._send_status_notification()
                
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Status notification loop error: {e}")
    
    async def _queue_monitor_loop(self):
        """Background queue monitoring loop."""
        while True:
            try:
                await asyncio.sleep(self.queue_monitor_interval)
                
                # Send queue status update
                await self._send_queue_notification()
                
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Queue monitor loop error: {e}")
    
    # Utility Methods
    
    async def _cancel_current_operation(self, reason: str):
        """Cancel current operation."""
        if self.current_operation_id:
            self.operation_status = OfflineOperationStatus.ERROR
            self.operation_error = f"Cancelled: {reason}"
            self.current_operation_id = None
    
    # Service Interface
    
    def set_notify_callback(self, callback: Callable[[str, bytes], None]):
        """Set notification callback."""
        self.notify_callback = callback
    
    def get_service_definition(self) -> Dict[str, Any]:
        """Get GATT service definition."""
        return self.service_data
    
    def get_offline_statistics(self) -> Dict[str, Any]:
        """Get offline service statistics."""
        return {
            "offline_service": {
                "is_running": self.status_task is not None and not self.status_task.done(),
                "current_operation_id": self.current_operation_id,
                "operation_status": self.operation_status.value,
                "operation_progress": self.operation_progress,
                "active_sync_id": self.active_sync_id,
                "notification_interval": self.status_notify_interval,
                "statistics": self.stats.copy()
            }
        }
    
    async def cleanup(self):
        """Clean up service resources."""
        await self.stop()
        
        # Clear state
        self.current_operation_id = None
        self.operation_status = OfflineOperationStatus.IDLE
        self.active_sync_id = None
        self.operation_result = None
        self.operation_error = None
        
        logger.info("OfflineService cleanup completed")


# Example usage and testing
if __name__ == "__main__":
    import asyncio
    from unittest.mock import MagicMock, AsyncMock
    
    async def test_offline_service():
        """Test offline service."""
        # Create mock dependencies
        offline_manager = MagicMock()
        config_synchronizer = MagicMock()
        
        # Mock async methods
        offline_manager.get_queue_status = AsyncMock(return_value={
            "network_status": "online",
            "pending_count": 5,
            "processing_count": 2,
            "failed_count": 1,
            "statistics": {}
        })
        offline_manager.queue_config = AsyncMock(return_value="config_123")
        offline_manager.network_monitor = MagicMock()
        offline_manager.network_monitor.status = NetworkStatus.ONLINE
        
        config_synchronizer.start_sync = AsyncMock(return_value="sync_123")
        
        # Create service
        service = OfflineService(offline_manager, config_synchronizer)
        
        # Set notification callback
        notifications = []
        def notify_callback(uuid, data):
            notifications.append((uuid, data))
        
        service.set_notify_callback(notify_callback)
        
        # Start service
        await service.start()
        
        try:
            # Test service definition
            service_def = service.get_service_definition()
            print(f"Service UUID: {service_def['uuid']}")
            print(f"Characteristics: {len(service_def['characteristics'])}")
            
            # Test offline status read
            status_data = await service.handle_read_offline_status()
            print(f"Offline status data length: {len(status_data)} bytes")
            
            # Test queue config command
            config_params = json.dumps({
                "device_id": "device_001",
                "config_data": {"wifi": {"ssid": "TestNetwork"}},
                "operation_type": "update",
                "priority": 5
            }).encode('utf-8')
            
            queue_cmd = bytes([OfflineCommand.QUEUE_CONFIG.value]) + config_params
            success = await service.handle_write_offline_control(queue_cmd)
            print(f"Queue config command: {'success' if success else 'failed'}")
            
            # Test sync command
            sync_params = json.dumps({
                "strategy": "incremental_sync",
                "direction": "bidirectional"
            }).encode('utf-8')
            
            sync_cmd = bytes([SyncCommand.START_SYNC.value]) + sync_params
            success = await service.handle_write_sync_control(sync_cmd)
            print(f"Start sync command: {'success' if success else 'failed'}")
            
            # Wait for command processing
            await asyncio.sleep(2)
            
            # Test queue status read
            queue_data = await service.handle_read_queue_status()
            print(f"Queue status data length: {len(queue_data)} bytes")
            
            # Get statistics
            stats = service.get_offline_statistics()
            print(f"Service statistics:")
            for key, value in stats["offline_service"].items():
                print(f"  {key}: {value}")
            
            print(f"Received {len(notifications)} notifications")
            
        finally:
            await service.cleanup()
    
    # Run test
    asyncio.run(test_offline_service())